﻿using System;
using System.Globalization;
using System.Text;

namespace GeoAPI.Geometries
{
	[Serializable]
	public class Envelope : IEnvelope, ICloneable, IComparable, IComparable<IEnvelope>, IEquatable<IEnvelope>, IEquatable<Envelope>, IComparable<Envelope>, IIntersectable<Envelope>, IExpandable<Envelope>
	{
		public static bool Intersects(Coordinate p1, Coordinate p2, Coordinate q)
		{
			return q.X >= ((p1.X < p2.X) ? p1.X : p2.X) && q.X <= ((p1.X > p2.X) ? p1.X : p2.X) && q.Y >= ((p1.Y < p2.Y) ? p1.Y : p2.Y) && q.Y <= ((p1.Y > p2.Y) ? p1.Y : p2.Y);
		}

		public static bool Intersects(Coordinate p1, Coordinate p2, Coordinate q1, Coordinate q2)
		{
			double num = Math.Min(p1.X, p2.X);
			double num2 = Math.Max(q1.X, q2.X);
			if (num > num2)
			{
				return false;
			}
			double num3 = Math.Min(q1.X, q2.X);
			double num4 = Math.Max(p1.X, p2.X);
			if (num4 < num3)
			{
				return false;
			}
			num = Math.Min(p1.Y, p2.Y);
			num2 = Math.Max(q1.Y, q2.Y);
			if (num > num2)
			{
				return false;
			}
			num3 = Math.Min(q1.Y, q2.Y);
			num4 = Math.Max(p1.Y, p2.Y);
			return num4 >= num3;
		}

		public Envelope()
		{
			this.Init();
		}

		public Envelope(double x1, double x2, double y1, double y2)
		{
			this.Init(x1, x2, y1, y2);
		}

		public Envelope(Coordinate p1, Coordinate p2)
		{
			this.Init(p1.X, p2.X, p1.Y, p2.Y);
		}

		public Envelope(Coordinate p)
		{
			this.Init(p.X, p.X, p.Y, p.Y);
		}

		public Envelope(Envelope env)
		{
			this.Init(env);
		}

		public void Init()
		{
			this.SetToNull();
		}

		public void Init(double x1, double x2, double y1, double y2)
		{
			if (x1 < x2)
			{
				this._minx = x1;
				this._maxx = x2;
			}
			else
			{
				this._minx = x2;
				this._maxx = x1;
			}
			if (y1 < y2)
			{
				this._miny = y1;
				this._maxy = y2;
				return;
			}
			this._miny = y2;
			this._maxy = y1;
		}

		public void Init(Coordinate p1, Coordinate p2)
		{
			this.Init(p1.X, p2.X, p1.Y, p2.Y);
		}

		public void Init(Coordinate p)
		{
			this.Init(p.X, p.X, p.Y, p.Y);
		}

		public void Init(Envelope env)
		{
			this._minx = env.MinX;
			this._maxx = env.MaxX;
			this._miny = env.MinY;
			this._maxy = env.MaxY;
		}

		public void SetToNull()
		{
			this._minx = 0.0;
			this._maxx = -1.0;
			this._miny = 0.0;
			this._maxy = -1.0;
		}

		public bool IsNull
		{
			get
			{
				return this._maxx < this._minx;
			}
		}

		public double Width
		{
			get
			{
				if (this.IsNull)
				{
					return 0.0;
				}
				return this._maxx - this._minx;
			}
		}

		public double Height
		{
			get
			{
				if (this.IsNull)
				{
					return 0.0;
				}
				return this._maxy - this._miny;
			}
		}

		public double MinX
		{
			get
			{
				return this._minx;
			}
		}

		public double MaxX
		{
			get
			{
				return this._maxx;
			}
		}

		public double MinY
		{
			get
			{
				return this._miny;
			}
		}

		public double MaxY
		{
			get
			{
				return this._maxy;
			}
		}

		public double Area
		{
			get
			{
				return this.Width * this.Height;
			}
		}

		public void ExpandBy(double distance)
		{
			this.ExpandBy(distance, distance);
		}

		public void ExpandBy(double deltaX, double deltaY)
		{
			if (this.IsNull)
			{
				return;
			}
			this._minx -= deltaX;
			this._maxx += deltaX;
			this._miny -= deltaY;
			this._maxy += deltaY;
			if (this._minx > this._maxx || this._miny > this._maxy)
			{
				this.SetToNull();
			}
		}

		public double MinExtent
		{
			get
			{
				if (this.IsNull)
				{
					return 0.0;
				}
				double width = this.Width;
				double height = this.Height;
				if (width < height)
				{
					return width;
				}
				return height;
			}
		}

		public double MaxExtent
		{
			get
			{
				if (this.IsNull)
				{
					return 0.0;
				}
				double width = this.Width;
				double height = this.Height;
				if (width > height)
				{
					return width;
				}
				return height;
			}
		}

		public void ExpandToInclude(Coordinate p)
		{
			this.ExpandToInclude(p.X, p.Y);
		}

		public void ExpandToInclude(double x, double y)
		{
			if (this.IsNull)
			{
				this._minx = x;
				this._maxx = x;
				this._miny = y;
				this._maxy = y;
				return;
			}
			if (x < this._minx)
			{
				this._minx = x;
			}
			if (x > this._maxx)
			{
				this._maxx = x;
			}
			if (y < this._miny)
			{
				this._miny = y;
			}
			if (y > this._maxy)
			{
				this._maxy = y;
			}
		}

		public void ExpandToInclude(Envelope other)
		{
			if (other.IsNull)
			{
				return;
			}
			if (this.IsNull)
			{
				this._minx = other.MinX;
				this._maxx = other.MaxX;
				this._miny = other.MinY;
				this._maxy = other.MaxY;
				return;
			}
			if (other.MinX < this._minx)
			{
				this._minx = other.MinX;
			}
			if (other.MaxX > this._maxx)
			{
				this._maxx = other.MaxX;
			}
			if (other.MinY < this._miny)
			{
				this._miny = other.MinY;
			}
			if (other.MaxY > this._maxy)
			{
				this._maxy = other.MaxY;
			}
		}

		public Envelope ExpandedBy(Envelope other)
		{
			if (other.IsNull)
			{
				return this;
			}
			if (this.IsNull)
			{
				return other;
			}
			double x = (other._minx < this._minx) ? other._minx : this._minx;
			double x2 = (other._maxx > this._maxx) ? other._maxx : this._maxx;
			double y = (other._miny < this._miny) ? other._miny : this._miny;
			double y2 = (other._maxy > this._maxy) ? other._maxy : this._maxy;
			return new Envelope(x, x2, y, y2);
		}

		public void Translate(double transX, double transY)
		{
			if (this.IsNull)
			{
				return;
			}
			this.Init(this.MinX + transX, this.MaxX + transX, this.MinY + transY, this.MaxY + transY);
		}

		public Coordinate Centre
		{
			get
			{
				if (!this.IsNull)
				{
					return new Coordinate((this.MinX + this.MaxX) / 2.0, (this.MinY + this.MaxY) / 2.0);
				}
				return null;
			}
		}

		public Envelope Intersection(Envelope env)
		{
			if (this.IsNull || env.IsNull || !this.Intersects(env))
			{
				return new Envelope();
			}
			return new Envelope(Math.Max(this.MinX, env.MinX), Math.Min(this.MaxX, env.MaxX), Math.Max(this.MinY, env.MinY), Math.Min(this.MaxY, env.MaxY));
		}

		public bool Intersects(Envelope other)
		{
			return !this.IsNull && !other.IsNull && (other.MinX <= this._maxx && other.MaxX >= this._minx && other.MinY <= this._maxy) && other.MaxY >= this._miny;
		}

		[Obsolete("Use Intersects instead")]
		public bool Overlaps(Envelope other)
		{
			return this.Intersects(other);
		}

		[Obsolete("Use Intersects instead")]
		public bool Overlaps(Coordinate p)
		{
			return this.Intersects(p);
		}

		[Obsolete("Use Intersects instead")]
		public bool Overlaps(double x, double y)
		{
			return this.Intersects(x, y);
		}

		public bool Intersects(Coordinate p)
		{
			return this.Intersects(p.X, p.Y);
		}

		public bool Intersects(double x, double y)
		{
			return x <= this._maxx && x >= this._minx && y <= this._maxy && y >= this._miny;
		}

		public bool Contains(Envelope other)
		{
			return this.Covers(other);
		}

		public bool Contains(Coordinate p)
		{
			return this.Covers(p);
		}

		public bool Contains(double x, double y)
		{
			return this.Covers(x, y);
		}

		public bool Covers(double x, double y)
		{
			return !this.IsNull && (x >= this._minx && x <= this._maxx && y >= this._miny) && y <= this._maxy;
		}

		public bool Covers(Coordinate p)
		{
			return this.Covers(p.X, p.Y);
		}

		public bool Covers(Envelope other)
		{
			return !this.IsNull && !other.IsNull && (other.MinX >= this._minx && other.MaxX <= this._maxx && other.MinY >= this._miny) && other.MaxY <= this._maxy;
		}

		public double Distance(Envelope env)
		{
			if (this.Intersects(env))
			{
				return 0.0;
			}
			double num = 0.0;
			if (this._maxx < env.MinX)
			{
				num = env.MinX - this._maxx;
			}
			else if (this._minx > env.MaxX)
			{
				num = this._minx - env.MaxX;
			}
			double num2 = 0.0;
			if (this._maxy < env.MinY)
			{
				num2 = env.MinY - this._maxy;
			}
			else if (this._miny > env.MaxY)
			{
				num2 = this._miny - env.MaxY;
			}
			if (num == 0.0)
			{
				return num2;
			}
			if (num2 == 0.0)
			{
				return num;
			}
			return Math.Sqrt(num * num + num2 * num2);
		}

		public override bool Equals(object other)
		{
			if (other == null)
			{
				return false;
			}
			Envelope envelope = other as Envelope;
			if (envelope != null)
			{
				return this.Equals(envelope);
			}
			return other is IEnvelope && ((IEquatable<IEnvelope>)this).Equals((IEnvelope)other);
		}

		public bool Equals(Envelope other)
		{
			if (this.IsNull)
			{
				return other.IsNull;
			}
			return this._maxx == other.MaxX && this._maxy == other.MaxY && this._minx == other.MinX && this._miny == other.MinY;
		}

		public int CompareTo(object other)
		{
			return this.CompareTo((Envelope)other);
		}

		public int CompareTo(Envelope env)
		{
			env = (env ?? new Envelope());
			if (this.IsNull)
			{
				if (env.IsNull)
				{
					return 0;
				}
				return -1;
			}
			else
			{
				if (env.IsNull)
				{
					return 1;
				}
				if (this.MinX < env.MinX)
				{
					return -1;
				}
				if (this.MinX > env.MinX)
				{
					return 1;
				}
				if (this.MinY < env.MinY)
				{
					return -1;
				}
				if (this.MinY > env.MinY)
				{
					return 1;
				}
				if (this.MaxX < env.MaxX)
				{
					return -1;
				}
				if (this.MaxX > env.MaxX)
				{
					return 1;
				}
				if (this.MaxY < env.MaxY)
				{
					return -1;
				}
				if (this.MaxY > env.MaxY)
				{
					return 1;
				}
				return 0;
			}
		}

		public override int GetHashCode()
		{
			int num = 17;
			num = 37 * num + Envelope.GetHashCode(this._minx);
			num = 37 * num + Envelope.GetHashCode(this._maxx);
			num = 37 * num + Envelope.GetHashCode(this._miny);
			return 37 * num + Envelope.GetHashCode(this._maxy);
		}

		private static int GetHashCode(double value)
		{
			long num = BitConverter.DoubleToInt64Bits(value);
			return (int)(num ^ num >> 32);
		}

		public override string ToString()
		{
			StringBuilder stringBuilder = new StringBuilder("Env[");
			if (this.IsNull)
			{
				stringBuilder.Append("Null]");
			}
			else
			{
				stringBuilder.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}, ", new object[]
				{
					this._minx,
					this._maxx
				});
				stringBuilder.AppendFormat(NumberFormatInfo.InvariantInfo, "{0:R} : {1:R}]", new object[]
				{
					this._miny,
					this._maxy
				});
			}
			return stringBuilder.ToString();
		}

		object ICloneable.Clone()
		{
			return this.Clone();
		}

		public Envelope Clone()
		{
			if (this.IsNull)
			{
				return new Envelope();
			}
			return new Envelope(this._minx, this._maxx, this._miny, this._maxy);
		}

		IEnvelope IEnvelope.Union(IPoint point)
		{
			return ((IEnvelope)this).Union(point.Coordinate);
		}

		IEnvelope IEnvelope.Union(ICoordinate coord)
		{
			IEnvelope envelope = this.Clone();
			envelope.ExpandToInclude(coord);
			return envelope;
		}

		IEnvelope IEnvelope.Union(IEnvelope box)
		{
			if (box.IsNull)
			{
				return this;
			}
			if (this.IsNull)
			{
				return box;
			}
			return new Envelope(Math.Min(this._minx, box.MinX), Math.Max(this._maxx, box.MaxX), Math.Min(this._miny, box.MinY), Math.Max(this._maxy, box.MaxY));
		}

		void IEnvelope.SetCentre(ICoordinate centre)
		{
			((IEnvelope)this).SetCentre(centre, this.Width, this.Height);
		}

		void IEnvelope.SetCentre(IPoint centre)
		{
			((IEnvelope)this).SetCentre(centre.Coordinate, this.Width, this.Height);
		}

		void IEnvelope.SetCentre(double width, double height)
		{
			((IEnvelope)this).SetCentre(this.Centre, width, height);
		}

		void IEnvelope.SetCentre(IPoint centre, double width, double height)
		{
			((IEnvelope)this).SetCentre(centre.Coordinate, width, height);
		}

		void IEnvelope.SetCentre(ICoordinate centre, double width, double height)
		{
			this._minx = centre.X - width / 2.0;
			this._maxx = centre.X + width / 2.0;
			this._miny = centre.Y - height / 2.0;
			this._maxy = centre.Y + height / 2.0;
		}

		void IEnvelope.Zoom(double perCent)
		{
			double width = this.Width * perCent / 100.0;
			double height = this.Height * perCent / 100.0;
			((IEnvelope)this).SetCentre(width, height);
		}

		void IEnvelope.Init()
		{
			this.SetToNull();
		}

		void IEnvelope.Init(ICoordinate p1, ICoordinate p2)
		{
			this.Init(p1.X, p2.X, p1.Y, p2.Y);
		}

		void IEnvelope.Init(ICoordinate p)
		{
			this.Init(p.X, p.X, p.Y, p.Y);
		}

		void IEnvelope.Init(IEnvelope env)
		{
			this._minx = env.MinX;
			this._maxx = env.MaxX;
			this._miny = env.MinY;
			this._maxy = env.MaxY;
		}

		void IEnvelope.ExpandToInclude(ICoordinate p)
		{
			this.ExpandToInclude(p.X, p.Y);
		}

		void IEnvelope.ExpandToInclude(IEnvelope other)
		{
			if (other.IsNull)
			{
				return;
			}
			if (this.IsNull)
			{
				this._minx = other.MinX;
				this._maxx = other.MaxX;
				this._miny = other.MinY;
				this._maxy = other.MaxY;
				return;
			}
			if (other.MinX < this._minx)
			{
				this._minx = other.MinX;
			}
			if (other.MaxX > this._maxx)
			{
				this._maxx = other.MaxX;
			}
			if (other.MinY < this._miny)
			{
				this._miny = other.MinY;
			}
			if (other.MaxY > this._maxy)
			{
				this._maxy = other.MaxY;
			}
		}

		ICoordinate IEnvelope.Centre
		{
			get
			{
				if (this.IsNull)
				{
					return null;
				}
				return new Coordinate((this.MinX + this.MaxX) / 2.0, (this.MinY + this.MaxY) / 2.0);
			}
		}

		IEnvelope IEnvelope.Intersection(IEnvelope env)
		{
			if (this.IsNull || env.IsNull || !((IEnvelope)this).Intersects(env))
			{
				return new Envelope();
			}
			return new Envelope(Math.Max(this.MinX, env.MinX), Math.Min(this.MaxX, env.MaxX), Math.Max(this.MinY, env.MinY), Math.Min(this.MaxY, env.MaxY));
		}

		bool IEnvelope.Intersects(IEnvelope other)
		{
			return !this.IsNull && !other.IsNull && (other.MinX <= this._maxx && other.MaxX >= this._minx && other.MinY <= this._maxy) && other.MaxY >= this._miny;
		}

		[Obsolete("Use Intersects instead")]
		bool IEnvelope.Overlaps(IEnvelope other)
		{
			return ((IEnvelope)this).Intersects(other);
		}

		[Obsolete("Use Intersects instead")]
		bool IEnvelope.Overlaps(ICoordinate p)
		{
			return ((IEnvelope)this).Intersects(p);
		}

		bool IEnvelope.Intersects(ICoordinate p)
		{
			return this.Intersects(p.X, p.Y);
		}

		bool IEnvelope.Contains(IEnvelope other)
		{
			return ((IEnvelope)this).Covers(other);
		}

		bool IEnvelope.Contains(ICoordinate p)
		{
			return ((IEnvelope)this).Covers(p);
		}

		bool IEnvelope.Covers(ICoordinate p)
		{
			return this.Covers(p.X, p.Y);
		}

		bool IEnvelope.Covers(IEnvelope other)
		{
			return !this.IsNull && !other.IsNull && (other.MinX >= this._minx && other.MaxX <= this._maxx && other.MinY >= this._miny) && other.MaxY <= this._maxy;
		}

		double IEnvelope.Distance(IEnvelope env)
		{
			if (((IEnvelope)this).Intersects(env))
			{
				return 0.0;
			}
			double num = 0.0;
			if (this._maxx < env.MinX)
			{
				num = env.MinX - this._maxx;
			}
			else if (this._minx > env.MaxX)
			{
				num = this._minx - env.MaxX;
			}
			double num2 = 0.0;
			if (this._maxy < env.MinY)
			{
				num2 = env.MinY - this._maxy;
			}
			else if (this._miny > env.MaxY)
			{
				num2 = this._miny - env.MaxY;
			}
			if (num == 0.0)
			{
				return num2;
			}
			if (num2 == 0.0)
			{
				return num;
			}
			return Math.Sqrt(num * num + num2 * num2);
		}

		bool IEquatable<IEnvelope>.Equals(IEnvelope other)
		{
			if (this.IsNull)
			{
				return other.IsNull;
			}
			return this._maxx == other.MaxX && this._maxy == other.MaxY && this._minx == other.MinX && this._miny == other.MinY;
		}

		int IComparable<IEnvelope>.CompareTo(IEnvelope other)
		{
			if (this.IsNull && other.IsNull)
			{
				return 0;
			}
			if (!this.IsNull && other.IsNull)
			{
				return 1;
			}
			if (this.IsNull && !other.IsNull)
			{
				return -1;
			}
			if (this.Area > other.Area)
			{
				return 1;
			}
			if (this.Area < other.Area)
			{
				return -1;
			}
			return 0;
		}

		public static Envelope Parse(string envelope)
		{
			if (string.IsNullOrEmpty(envelope))
			{
				throw new System.ArgumentNullException("envelope");
			}
			if (!envelope.StartsWith("Env[") || !envelope.EndsWith("]"))
			{
				throw new ArgumentException("Not a valid envelope string", "envelope");
			}
			envelope = envelope.Substring(4, envelope.Length - 5);
			if (envelope == "Null")
			{
				return new Envelope();
			}
			double[] array = new double[4];
			string[] array2 = new string[]
			{
				"x",
				"y"
			};
			int num = 0;
			string[] array3 = envelope.Split(new char[]
			{
				','
			});
			if (array3.Length != 2)
			{
				throw new ArgumentException("Does not provide two ranges", "envelope");
			}
			foreach (string text in array3)
			{
				string[] array5 = text.Split(new char[]
				{
					':'
				});
				if (array5.Length != 2)
				{
					throw new ArgumentException("Does not provide just min and max values", "envelope");
				}
				if (!double.TryParse(array5[0].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out array[2 * num]))
				{
					throw new ArgumentException(string.Format("Could not parse min {0}-Ordinate", array2[num]), "envelope");
				}
				if (!double.TryParse(array5[1].Trim(), NumberStyles.Number, NumberFormatInfo.InvariantInfo, out array[2 * num + 1]))
				{
					throw new ArgumentException(string.Format("Could not parse max {0}-Ordinate", array2[num]), "envelope");
				}
				num++;
			}
			return new Envelope(array[0], array[1], array[2], array[3]);
		}

		private double _minx;

		private double _maxx;

		private double _miny;

		private double _maxy;
	}
}
